package me.zhenqu.redis;

import redis.clients.jedis.Tuple;

import java.util.Collection;
import java.util.List;
import java.util.Set;

/**
 * Created at 11-8-3
 *
 * @author yonghui.chen
 */
public class ZorderRedisList {
    private int size;
    private String category;
    private RedisOperator operator;
    private ScoreListInitializer initializer;

    public ZorderRedisList(String category, int size) {
        this.category = "zlist_" + category;
        operator = RedisOperator.getInstance();
        this.size = size;
    }

    public ZorderRedisList(String category, int size, ScoreListInitializer initializer) {
        this.category = "zlist_" + category;
        operator = RedisOperator.getInstance();
        this.size = size;
        this.initializer = initializer;
    }

    public ZorderRedisList(String redisServer, String category, int size, ScoreListInitializer initializer) {
        operator = RedisOperator.getOperator(redisServer);
        this.category = "zlist_" + category;
        operator = RedisOperator.getInstance();
        this.size = size;
        this.initializer = initializer;
    }

    public ZorderRedisList(String redisServer, String category, int size) {
        operator = RedisOperator.getOperator(redisServer);
        this.category = "zlist_" + category;
        this.size = size;
    }

    public void appendItem(String key, String item, double score) {
        long size = operator.sizeOfSet(category, key);
        if (size > this.size) {
            operator.removeByRank(category, key, 0, 0);
        }
        operator.add(category, key, score, item);
    }

    public void appendItems(String key, Collection<ScoredValue> values) {
        for (ScoredValue value : values) {
            operator.add(category, key, value.getScore(), value.getValue());
        }
        long size = operator.sizeOfSet(category, key);
        if (size > this.size) {
            operator.removeByRank(category, key, 0, (int) (size - this.size));
        }
    }

    private void doInit(String key) {
        long s = 0;
        List<ScoredValue> initValue = initializer.getInitValue(key);
        for (ScoredValue val : initValue) {
            s++;
            operator.add(category, key, val.getScore(), val.getValue());
            if (s == size - 1) break;
        }
    }

    public void clear(String key) {
        operator.remove(category, key);
    }

    public Set<String> items(String key, int offset, int size) {
        if (initializer != null) {
            long s = operator.sizeOfSet(category, key);
            if (s == 0) {
                doInit(key);
            }
        }
//        return null;
        return operator.reverse(category, key, offset, offset + size - 1);
    }

    public Set<String> items(String key, String value, int size) {      // from value
        if (initializer != null) {
            long s = operator.sizeOfSet(category, key);
            if (s == 0) {
                doInit(key);
            }
        }
        int idx = (int) operator.lastIndexOf(category, key, value);
        if(idx < 0) idx = 0;
//        return null;
        return operator.reverse(category, key, idx + 1, idx + size);
    }

    public void remove(String key, String value) {
        if (initializer != null) {
            long s = operator.sizeOfSet(category, key);
            if (s == 0) {
                doInit(key);
            }
        }
        operator.rangeRemove(category, key, value);
    }

    public long countBetween(String key, double from, double to) {
        if (initializer != null) {
            long s = operator.sizeOfSet(category, key);
            if (s == 0) {
                doInit(key);
            }
        }

        if (from > to) {
            return operator.count(category, key, to, from);
        } else {
            return operator.count(category, key, from, to);
        }
    }

    public Set<String> items(String key, double from, double to, int offset, int size) {
        if (initializer != null) {
            long s = operator.sizeOfSet(category, key);
            if (s == 0) {
                doInit(key);
            }
        }

        if (from < to) {
            return operator.reverseRange(category, key, to, from, offset, size);
        } else {
            return operator.reverseRange(category, key, from, to, offset, size);
        }
    }

    public Set<Tuple> itemsWithScore(String key, double from, double to, int offset, int size) {
        if (initializer != null) {
            long s = operator.sizeOfSet(category, key);
            if (s == 0) {
                doInit(key);
            }
        }

        if (from < to) {
            return operator.reverseRangeWithScore(category, key, to, from, offset, size);
        } else {
            return operator.reverseRangeWithScore(category, key, from, to, offset, size);
        }
    }
}
